import type { AppRouter } from '@/utils/api';
import { trpcPureClient } from '@/utils/api';
import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
import type Uppy from '@uppy/core';
import type { UppyEventMap } from '@uppy/core';
import { useEffect, useMemo, useRef, useState } from 'react';
import { usePasteFile } from '../hooks/usePasteFile';
import { LocalFileItem, RemoteFileItem } from './FileItem';
import type { inferRouterOutputs } from '@trpc/server';
import { cn } from '@/lib/utils';
import { Button } from '../ui/Button';
import { ScrollArea } from '../ui/ScrollArea';
import type { FilesOrderByColumn } from '@/server/routes/file';
import { CopyUrl, DeleteFile } from './FileItemAction';

type FileResult =
  inferRouterOutputs<AppRouter>['file']['infinityQueryFiles']['items'];

export function FileList({
  uppy,
  orderBy,
  withDeleted,
  appId,
}: {
  uppy: Uppy;
  orderBy: FilesOrderByColumn;
  withDeleted: boolean;
  appId: string;
}) {
  const queryKey = useMemo(() => {
    return ['store_files', orderBy?.field, orderBy?.order, withDeleted];
  }, [orderBy?.field, orderBy?.order, withDeleted]);

  const {
    data: infinityQueryData,
    isLoading,
    refetch: refetchFileList,
    fetchNextPage,
    hasNextPage,
  } = useInfiniteQuery({
    queryKey,
    queryFn: (ctx) =>
      trpcPureClient.file.infinityQueryFiles.query({
        ...ctx.pageParam,
        orderBy,
        withDeleted,
        appId,
      }),
    initialPageParam: {
      limit: 3,
    },
    getNextPageParam: (resp) => {
      if (!resp.nextCursor) return null;

      return {
        limit: 3,
        cursor: resp.nextCursor,
      };
    },
    refetchOnReconnect: false,
    refetchOnWindowFocus: false,
    refetchOnMount: false,
  });

  const [queryClient] = useState(useQueryClient());

  const bottomRef = useRef<HTMLDivElement>(null);

  const filesList = useMemo(
    () =>
      infinityQueryData
        ? infinityQueryData.pages.reduce<FileResult>((result, page) => {
            return [...result, ...page.items];
          }, [])
        : [],
    [infinityQueryData]
  );

  useEffect(() => {
    const handler: UppyEventMap<{}, {}>['upload-success'] = async (
      file,
      resp
    ) => {
      if (file) {
        await trpcPureClient.file.saveFile
          .mutate({
            name: file.data instanceof File ? file.data.name : 'test',
            path: resp.uploadURL ?? '',
            type: file.data.type,
            appId,
          })
          .then((resp) => {
            queryClient.setQueryData(
              queryKey,
              (prev: typeof infinityQueryData) => {
                if (!prev) return prev;

                return {
                  ...prev,
                  pages: prev.pages.map((page, index) => {
                    if (index === 0) {
                      return {
                        ...page,
                        items: [resp, ...page.items],
                      };
                    }

                    return page;
                  }),
                };
              }
            );
          });
      }
    };

    const uploadProgressHandler: UppyEventMap<{}, {}>['upload'] = (
      _,
      files
    ) => {
      setUploadingFiles((currentFiles) => [...currentFiles, ...files]);
    };

    const completeHandler = () => {
      setUploadingFiles([]);
    };

    uppy.on('complete', completeHandler);

    uppy.on('upload', uploadProgressHandler);

    uppy.on('upload-success', handler);

    return () => {
      uppy.off('upload-success', handler);
      uppy.off('upload', uploadProgressHandler);
      uppy.off('complete', completeHandler);
    };
  }, [
    appId,
    orderBy?.field,
    orderBy?.order,
    queryClient,
    queryKey,
    refetchFileList,
    uppy,
  ]);

  usePasteFile({
    onFilesPaste: (files) => {
      uppy.addFiles(
        files.map((file) => {
          return {
            data: file,
            name: file.name,
          };
        })
      );
    },
  });

  const [uploadingFiles, setUploadingFiles] = useState<
    ReturnType<typeof uppy.getFile>[]
  >([]);

  // ----> intersection

  useEffect(() => {
    if (bottomRef.current) {
      const observer = new IntersectionObserver(
        (e) => {
          if (e[0].intersectionRatio > 0.1) {
            fetchNextPage();
          }
        },
        {
          threshold: 0.1,
        }
      );

      const target = bottomRef.current;

      observer.observe(target);

      return () => {
        observer.unobserve(target);
        observer.disconnect();
      };
    }
  }, [fetchNextPage]);

  const handleFileDelete = (fileId: string) => {
    queryClient.setQueryData(queryKey, (prev: typeof infinityQueryData) => {
      if (!prev) return prev;

      return {
        ...prev,
        pages: prev.pages.map((page) => {
          return {
            ...page,
            items: page.items.filter(({ id }) => id !== fileId),
          };
        }),
      };
    });
  };

  return (
    <ScrollArea className="h-full">
      {isLoading && <div className="text-center">Loading</div>}
      <div className={cn('flex justify-center flex-wrap gap-4 relative')}>
        {uploadingFiles.length > 0 &&
          uploadingFiles.map((file) => {
            return (
              <div
                key={file.id}
                className="w-56 h-56 flex justify-center border items-center border-red-500"
              >
                <LocalFileItem file={file.data as File} />
              </div>
            );
          })}
        {filesList?.map((file) => {
          return (
            <div
              key={file.id}
              className="w-56 h-56 flex justify-center border items-center relative"
            >
              <div className="absolute inset-0 bg-background/30 justify-center items-center flex opacity-0 hover:opacity-100 transition-all">
                <CopyUrl id={file.id} />
                {!file.deletedAt && (
                  <DeleteFile
                    fileId={file.id}
                    onDeleteSuccess={(fileId) => {
                      handleFileDelete(fileId);
                    }}
                  />
                )}
              </div>
              <RemoteFileItem
                contentType={file.contentType}
                id={file.id}
                name={file.name}
              />
            </div>
          );
        })}
      </div>
      <div
        className={cn(
          'hidden justify-center p-8',
          filesList.length > 0 && 'flex'
        )}
        ref={bottomRef}
      >
        <Button
          variant="ghost"
          disabled={!hasNextPage}
          onClick={() => fetchNextPage()}
        >
          Load Next Page
        </Button>
      </div>
    </ScrollArea>
  );
}
